/*
* Copyright (c) Huawei Technologies Co., Ltd. 2019-2022. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Description:
* Author: huawei
* Create: 2019-10-15
*/

#include "dms_define.h"
#include "dms_common.h"
#include "dms_notifier.h"
#include "dms_sensor_type.h"
#include "soft_fault_define.h"
#include "davinci_interface.h"

void os_reset_report(u32 devid)
{
    int ret;
    int data_len;
    struct soft_fault os_init;
    const char *info = "OS init";

    data_len = strlen(info) + 1;
    ret = strcpy_s(os_init.data, DMS_MAX_EVENT_DATA_LENGTH, info);
    if (ret != 0) {
        soft_drv_err("String copy failed! (device=%u) \n", devid);
        return;
    }

    os_init.data_len = data_len;
    os_init.dev_id = devid;
    os_init.sensor_type = DMS_SEN_TYPE_GENERAL_SOFTWARE_FAULT;
    os_init.user_id = SF_SENSOR_OS;
    os_init.node_id = SF_USER_NODE_OS;
    os_init.sub_id = SF_SUB_ID0;
    os_init.err_type = OS_INIT;

    ret = soft_fault_event_handler(&os_init);
    if (ret != 0) {
        soft_drv_err("Soft fault report failed! (device=%u) \n", devid);
    }

    return;
}


void os_recover_report(u32 devid)
{
    int ret;
    int data_len;
    struct soft_fault os_init;
    const char *info = "OS init finised";

    data_len = strlen(info) + 1;
    ret = strcpy_s(os_init.data, DMS_MAX_EVENT_DATA_LENGTH, info);
    if (ret != 0) {
        soft_drv_err("String copy failed! (device=%u) \n", devid);
        return;
    }

    os_init.data_len = data_len;
    os_init.dev_id = devid;
    os_init.sensor_type = DMS_SEN_TYPE_GENERAL_SOFTWARE_FAULT;
    os_init.user_id = SF_SENSOR_OS;
    os_init.node_id = SF_USER_NODE_OS;
    os_init.sub_id = SF_SUB_ID0;
    os_init.err_type = OS_INIT_DONE;

    ret = soft_fault_event_handler(&os_init);
    if (ret != 0) {
        soft_drv_warn("Report OS init finisd failed! (device=%u; ret=%d) \n", devid, ret);
    }

    return;
}


STATIC int os_dev_node_config(unsigned int user_id, struct soft_dev *s_dev)
{
    int pid = current->tgid;
    struct dms_node_operations *soft_ops = soft_get_ops();
    struct dms_node s_node = SOFT_NODE_DEF(HAL_DMS_DEV_TYPE_OS_LINUX, "OS Linux", s_dev->dev_id,
        s_dev->node_id, soft_ops);
    struct dms_sensor_object_cfg s_cfg = SOFT_SENSOR_DEF(DMS_SEN_TYPE_GENERAL_SOFTWARE_FAULT, "OS init", 0UL,
        user_id, SF_SUB_ID0, 0xFFFF, 0xFDFF, SF_SENSOR_SCAN_TIME, soft_fault_event_scan, pid);

    s_cfg.private_data = ((unsigned long)s_dev->dev_id << SF_OFFSET_32BIT) | (user_id << SF_OFFSET_16BIT) |
        (s_dev->node_id << SF_OFFSET_8BIT) | SF_SUB_ID0;

    /* for kernel space, not used the pid, set default value -1 */
    s_cfg.pid = -1;
    s_node.pid = -1;
    s_dev->sensor_obj_num++;
    s_dev->sensor_obj_table[SF_SUB_ID0] = s_cfg;
    s_dev->dev_node = s_node;

    return 0;
}

int os_dev_register(u32 devid)
{
    int ret;
    unsigned int user_id = SF_SENSOR_OS;
    struct soft_dev_client *client = NULL;
    struct soft_dev *s_dev = NULL;
    struct drv_soft_ctrl *soft_ctrl = soft_get_ctrl();

    mutex_lock(&soft_ctrl->mutex[devid]);

    client = soft_ctrl->s_dev_t[devid][user_id];
    if (client->registered == 1) {
        soft_drv_warn("Node OS-Linux already registered, return. (dev_id=%u; registered=%u)\n",
            devid, client->registered);
        mutex_unlock(&soft_ctrl->mutex[devid]);
        return 0;
    }

    s_dev = kzalloc(sizeof(struct soft_dev), GFP_KERNEL | __GFP_ACCOUNT);
    if (s_dev == NULL) {
        soft_drv_err("kzalloc soft_dev failed.\n");
        mutex_unlock(&soft_ctrl->mutex[devid]);
        return -ENOMEM;
    }

    soft_one_dev_init(s_dev);
    s_dev->dev_id = devid;
    s_dev->node_id = SF_USER_NODE_OS;

    ret = os_dev_node_config(user_id, s_dev);
    if (ret != 0) {
        soft_drv_err("soft_fault configurate one node failed. (dev_id=%u; ret=%d)\n", devid, ret);
        goto ERROR;
    }

    ret = soft_register_one_node(s_dev);
    if (ret != 0) {
        soft_drv_err("soft_fault register one node failed. (dev_id=%u; ret=%d)\n", devid, ret);
        goto ERROR;
    }

    list_add(&s_dev->list, &client->head);
    client->user_id = user_id;
    client->registered = 1;
    client->node_num++;
    soft_ctrl->user_num[devid]++;
    mutex_unlock(&soft_ctrl->mutex[devid]);

    return 0;
ERROR:
    kfree(s_dev);
    s_dev = NULL;
    mutex_unlock(&soft_ctrl->mutex[devid]);
    return ret;
}

void os_dev_unregister(void)
{
    int i;
    struct soft_dev_client *client = NULL;
    struct drv_soft_ctrl *soft_ctrl = soft_get_ctrl();

    for (i = 0; i < DEV_ID_MAX; i++) {
        client = soft_ctrl->s_dev_t[i][SF_SENSOR_OS];
        soft_free_one_node(client, HAL_DMS_DEV_TYPE_OS_LINUX);
        soft_ctrl->user_num[i]--;
    }

    return;
}

int os_device_notifier_func(u32 devid)
{
    int ret = 0;

    if (devid >= DEVDRV_PF_DEV_MAX_NUM) {
        return 0;
    }
    ret = os_dev_register(devid);
    os_reset_report(devid);
    os_recover_report(devid);

    return ret;
}
